<?php 
/**
 * This file is part of DomAr.
 * 
 * DomAr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DomAr is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DomAr.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @link http://inditel.ee/
 * @copyright 2007-2009 Inditel Meedia OÜ
 * @author Oliver Leisalu <oliver@inditel.ee>
 */

/**
* @package DomAr
* @subpackage Relation
*/
class DomArSet extends DomArPlugin implements ArrayAccess, IteratorAggregate {
	
	/**
	* @param Array New objects in set will be put here
	*/
	protected $newObjects = array();
	/**
	* @param Int New objects unique key counter.
	*/
	protected $newObjectsI = 0;
	/**
	* Already loaded objects will be held here. Needed to save changes. 
	* @param Array 
	*/
	protected $loadedObjects = array();
	/**
	* @param Array
	*/
	protected $listeners;
	
	/**
	* @param DomArObject $parent
	* @param DomArProperty_HasMany $property
	*/
	public function __construct( DomArObject $parent = NULL, $property = NULL ) {
		parent::__construct( $parent, $property );
	}
	
	/**
	* Adds new object to set
	* Doesn't raise error if object is already added!
	* @param DomArObject $object
	* @return Int New object identifier.
	*/
	public function push( DomArObject $object ) {
		return $this->getProperty()->set_add( $this, $object );
	}
	
	/**
	* Sets objects in array to this list element and removes any previous items that existed on this set.
	* Useful for updating full list with new objects. Array elements must be DomArObjects or just id-s. Sample: array(2,6,8,12,32,5), array( DomArObject, DomArObject, ...)
	*
	* @param Array $ids
	*/
	public function set( array $ids = NULL ) {
		$this->getProperty()->set_set( $this, $ids );
	}
	/**
	* ArrayAccess method for adding items to list like $list[] = $item instead of $list->push( $item );
	* @param String $key
	* @param DomArObject $value
	*/
	public function offsetSet( $key, $value ) {
		$this->push( $value );
	}
	
	/**
	* Validates if item added to set is allowed.
	* @param DomArObject $object
	* @return Int 0 when item validates
	*/
	public function validate( DomArObject $object ) {
		return $this->getProperty()->set_validate( $this, $object );
	}
	
	/**
	* Save list
	*/
	public function save() {
		return $this->getProperty()->save( $this->getParent(), $this );
	}
	
	/**
	* Find item from list with id.
	* @param Int $id
	* @return DomArObject Null if object is not found
	*/
	public function find( $id ) {
		return $this->getProperty()->set_find( $this, $id );
	}
	
	/**
	* Returns query object to search from set items.
	* @return DomArQuery
	*/
	public function query() {
		return $this->getProperty()->set_query( $this );
	}
	
	/**
	* Returns dataquery object to search from set items and load data.
	* @param String $fields Fields that will be loaded.
	* @return DomArDataQuery
	*/
	public function dataQuery( $fields = 't.*' ) {
		return $this->getProperty()->set_dataQuery( $this, $fields );
	}
	
	/**
	* Returns idSetQuery object with id-s as keys and $field as values
	* @param String $field
	* @return DomArDataQuery
	*/
	public function idSetQuery( $field = 'id' ) {
		return $this->getProperty()->set_IdSetQuery( $this, $field );
	}
	
	/**
	* @return Int Nr of items in list
	*/
	public function count() {
		return $this->getProperty()->set_count( $this );
	}
	
	/**
	* Count items from list matching some conditions.
	* @return DomArDataQuery
	*/
	public function countQuery() {
		return $this->dataQuery('count(*) as n')->limit(1);
	}
	
	/**
	* Remove item from set
	* @param DomArObject $object
	* @return void
	*/
	public function remove( DomArObject $object ) {
		$this->getProperty()->set_remove( $this, $object );
	}
	
	/**
	* Remove item from set without making database query. Removes item from new items and loaded items list but not from database. This means that reloading list items reloades the item.
	* @param DomArObject $object
	*/
	public function unload( DomArObject $object  ) {
		$this->removeNewObject( $object );
		$this->removeLoadedObject( $object );
	}
	
	/**
	* IteratorAggregate method
	* @return DomArIterator
	*/
	public function getIterator() {
		if( $this->getParent()->id > 0 ) {
			if( empty($this->newObjects) ) {
				$iterator = $this->query()->commit();
			} else {
				$iterator = $this->query()->commit();
				$iterator->addNewObjects(true);
			}
		} else {
			$query = DomArQuery::c()->select('NULL')->from('(SELECT NULL)')->where('1=2');
			$iterator = new DomArIterator( $query, $this );
			$iterator->addNewObjects(true);
		}
		return $iterator;
	}
	
	/**
	* Return list items as array not as iterator object.
	* New objects are put to the end of array
	* @return Array
	*/
	public function toArray() {
		return $this->getIterator()->toArray();
	}
	
	/**
	* Return list items as array where keys are Id-s.
	* @return Array
	*/
	public function toIdArray() {
		$items  = array();
		foreach( $this->query()->commit() as $item )
			$items[$item->id] = $item;
		return $items;
	}

	/**
	* ArrayAccess method
	* @throws InvalidMethodCallException 
	*/
	public function offsetExists( $key ) {
		throw new InvalidMethodCallException('checking if exists from '.__CLASS__.' is not allowed!');
	}
	
	/**
	* ArrayAccess method
	* @throws InvalidMethodCallException 
	*/
	public function offsetGet( $key ) {
		throw new RuntimeException('getting from '.__CLASS__.' is not allowed!');
	}
		
	/**
	* ArrayAccess method
	* @throws InvalidMethodCallException 
	*/
	public function offsetUnset( $key ) {
		throw new RuntimeException('Unseting from '.__CLASS__.' is not allowed!');
	}
	
	
	/**
	* @return String Children table name.
	*/
	public function getChildrenTableName() {
		return $this->getProperty()->getChildrenTableName();
	}
	
	
	/**
	* @param DomArObject 
	*/
	public function addNewObject( DomArObject $object ) {
		$this->newObjects[$this->newObjectsI++] = $object;
		$this->changed();
		return $this->newObjectsI-1;
	}
	/**
	* @return Array New objects added to list.
	*/
	public function getNewObjects() {
		return $this->newObjects;
	}
	/**
	* Returns one object from new objects list by key.
	* @return DomArObject
	*/
	public function getNew( $key ) {
		return $this->newObjects[$key];
	}
	/**
	* Remove object from new objects list.
	*
	* @tricky This method must be protected and not accessed by user as it only removes object from new list but WILL NOT invoke ANY listeners!
	* @internal
	* @param DomArObject
	*/
	protected function removeNewObject( DomArObject $object ) {
		foreach( $this->newObjects as $i => $ob ) {
			if( $ob === $object )
				unset($this->newObjects[$i]);
		}
	}
	/**
	* Flushes new objects list.
	*/
	public function flushNewObjects() {
		$this->newObjects = array();
		$this->newObjectsI = 0;
	}
	
	public function isChildNew( DomArObject $object ) {
		foreach( $this->newObjects as $i => $ob ) {
			if( $ob === $object )
				return true;
		}
		return false;
	}
	
	
		
	/**
	* Adds object to loaded objects list. This method is used internally and should not be used!
	* @param DomArObject $object
	*/
	public function addLoadedObject( DomArObject $object ) {
		$this->loadedObjects[$object->getId()] = $object;
		$this->changed();
	}
	/**
	* Remove object from loaded objects list.
	* @param DomArObject
	*/
	protected function removeLoadedObject( DomArObject $object ) {
		unset($this->loadedObjects[$object->id]);
	}
	/**
	* Flushes loaded objects list.
	*/
	public function flushLoadedObjects() {
		$this->loadedObjects = array();
	}
	/**
	* @return Array Loaded objects list where key is id and value is object.
	*/
	public function getLoadedObjects() {
		return $this->loadedObjects;
	}

}
?>